// -*- coding: utf-8 -*-

#ifndef HYBRID_VAR_H
#define HYBRID_VAR_H

#include <vector>

#include <float.h>

#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>
#include <boost/mem_fn.hpp>

#include "vnode.h"

#include "elisa/elisa_defs.h"
#include "elisa/elisa_variable.h"
#include "elisa/elisa_model.h"
#include "elisa/elisa_and_model.h"
#include "elisa/elisa_constraint.h"
#include "elisa/elisa_reduction_factory.h"

//NAMESPACE_ELISA
namespace elisa {

/*
 * gaol::interval, elisa::Icode, v_bias::interval, v_blas::iVectorは直接使わないこと
 */

/// VNODE用区間
typedef v_bias::interval VNInterval;
/// VNODE用区間ベクトル
typedef v_blas::iVector  VNIntVec;

typedef boost::shared_ptr<VNInterval>	VNIntPtr;
typedef boost::shared_ptr<VNIntVec>		VNIVPtr;

/// VNODEが1ステップで計算する包囲 (時間区間, (a priori包囲, tight包囲))。
typedef std::pair<VNIVPtr, VNIVPtr>		VNEncl;
typedef std::pair<VNIntPtr, VNEncl>		VNPipe;

typedef boost::shared_ptr<vnodelp::FADBAD_AD>	ADPtr;
typedef boost::shared_ptr<VNIntVec>				IVPtr;

//typedef VNIntVec IcodeVec;


/**
 * @ingroup Hybrid
 * @class ContVarImpl
 * @brief Internal representation of ContVar
 */
class ContVarImpl : public RealVarImpl
{
public:
	ContVarImpl(Variable *var, SearchTree *st, const Interval& i) 
		: RealVarImpl(var, st, i) 
	{
	}

	/**
 	 * Give the type of the VariableImpl
 	 * @return a string given the name of the type
 	 */
	virtual std::string getType() const
	{
		return "ContVarImpl";
	}
};

/**
 * @ingroup Hybrid
 * @class ContVar
 * @brief Continuous variable
 */
class ContVar : public RealVar
{
public:
	/**
 	 * @brief constructor
 	 * build a new ContVar with given order of differentials
 	 */
 	ContVar(const VNInterval& value, const std::string& name = "", 
			const Interval& domain = Interval::Universe) 
		: RealVar(domain, name),
		  m_initValue(value)
	{}

	const VNInterval& getInitValue()
	{
		return m_initValue;
	}

	/**
 	 * @brief function to build the internal representation
 	 * it calls the factory class
 	 */
	virtual void buildImplementation(SearchTree *searchTree)
	{
		m_implementation = VarImplPtr(new ContVarImpl(this, searchTree, this->domain()));
	}

	virtual Variable *copy(SearchTree *searchTree) const
	{
		RealVar *v = new RealVar(this->domain(), this->m_name);
		v->buildImplementation(searchTree);
		return v;
	}

	static int logIndex;

protected:
	const VNInterval m_initValue;

	// copy protection
	ContVar(const ContVar &);
	ContVar & operator=(const ContVar &);
};

typedef std::vector<ContVar *> ContVarVec;


static VNInterval copyIV(const VNInterval & iv)
{
	return VNInterval(iv);
}


/**
 * @ingroup Hybrid
 * @class TimeImpl
 * @brief Internal representation of Time
 */
class TimeImpl : public RealVarImpl
{
public:
	TimeImpl(Variable *var, SearchTree *st, const Interval & t, int priority = 2) 
		: RealVarImpl(var, st, t, priority) 
	{
	}

	/**
 	 * Give the type of the VariableImpl
 	 * @return a string given the name of the type
 	 */
	virtual std::string getType() const
	{
		return "TimeImpl";
	}

	bool isSplittable() const;

	void split(const Interval & pivot, 
			   const VNIntVec  & ivL, const VNIntVec  & ivR)
	{
		Interval dom(domain());
		VNInterval tL(dom.inf(), pivot.sup());
		VNInterval tR(pivot.inf(), dom.sup());

		VariableImpl::splitBox<Interval>(Interval(tL), Interval(tR));
	}

private:
	// copy protection
	TimeImpl & operator=(const TimeImpl &);
	TimeImpl(const TimeImpl &);
};


/**
 * @ingroup Hybrid
 * @class Time
 * @brief Variable representing time
 */
class Time : public RealVar
{
public:
	/**
 	 * @brief build a new Time
 	 */
	Time(const Interval& time, ContVarVec& cvv, const double& excess = 0.0, 
		 const double tmax = 1.0e2,
		 const double lb = Interval::Universe.inf())

		 //: RealVar(time, "time"),
		 : RealVar(Interval(time.inf(), tmax), "time"),
		  m_contVars(cvv),
		  //m_initTime(time.inf()),
		  m_initTime(time.inf(), time.sup()),
		  //m_precision(DefaultPrecision),
		  m_lb(lb),
		  m_excess(0, excess)
	{
		//setDomain(time, cvv);		

		for (int i(0); i < cvv.size(); ++i) {
			m_initValue.push_back(cvv[i]->getInitValue());
		}
	}

	Time(const Interval& time, const double& excess = 0.0, const double& lb = Interval::Universe.inf())
		: RealVar(time, "time"),
		  m_initTime(time.inf()),
		  //m_precision(DefaultPrecision),
		  m_lb(lb),
		  m_excess(0, excess)
	{
		//setDomain(time, cvv);
	}

	/*
	Time(const Interval& dom)
		: RealVar(dom, "time"),
		  m_precision(DefaultPrecision)
	{
	}
	*/

	void addInitValue(ContVar& cv)
	{
		m_initValue.push_back(cv.getInitValue());
	}

	void addInitValue(ContVarVec& cvv)
	{
		for (int i(0); i < cvv.size(); ++i) {
			addInitValue(*cvv[i]);
		}
	}

	VNIntVec& getInitValue()
	{
		return m_initValue;
	}

	void setInitTime(const VNInterval& initTime)
	{
		m_initTime = initTime;
	}

	const VNInterval& getInitTime() const
	{
		return m_initTime;
	}

	const VNInterval& getExcess() const
	{
		return m_excess;
	}

	/**
 	 * @brief function to build the internal representation
 	 * it calls the factory class
 	 */
	virtual void buildImplementation(SearchTree *searchTree)
	{
		m_implementation = VarImplPtr(new TimeImpl(this, searchTree, this->domain()));
		// for debug
		//const FlowPipe & dom = dynamic_cast<TimeImpl *>(m_implementation.get())->domain();
	}

	virtual Variable *copy(SearchTree *searchTree) const
	{
		Time *v = new Time(this->domain());
		v->buildImplementation(searchTree);
		return v;
	}

	/// Set the precision for splitting
	static void setPrecision(double p)
	{
		m_precision = p;
	}

	/// Returns the precision for splitting
	const double& getPrecision() const
	{
		return m_precision;
	}

	double m_lb;

private:
	ContVarVec m_contVars;

	VNIntVec   m_initValue;
	VNInterval m_initTime;

	// the width of the initial time
	VNInterval m_excess;

	// @var used for splitting
	static double m_precision;

	static const double DefaultPrecision;

	// copy protection
	Time(const Time &);
	Time & operator=(const Time &);
};

//END_NAMESPACE_ELISA
}

#endif // HYBRID_VAR_H
